﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using DomainDriver.DomainModeling.DomainObjects;
using DomainDriver.DomainModeling.Queries;
using DomainDriver.DomainModeling.StorageManagers.DataSetStorage;
using DomainDriver.DomainModeling.StorageManagers.EntityFrameworkStorage;
using AutomotivePricingModel.ValueObjects.YearlyFinancialAssessments;
using ObjectRow = AutomotivePricingModel.AutomotivePricingDataSet.CompaniesRow;
using NestedObjectRow = AutomotivePricingModel.AutomotivePricingDataSet.YearlyFinancialAssessmentsRow;

namespace AutomotivePricingModel.Companies
{
    public partial class Company : IEfEntity<Guid>, IEfAggregate<Company>, IDataSetSerializable<AutomotivePricingDataSet, DataRow>
    {
        #region ADO .NET Entities Framework Storage

        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public Guid EF_Key
        {
            get { return m_Key; }
            set { m_Key = value; }
        }

        [Required]
        //[Association("Assc_Company_YearlyFinancialAssessment", "EF_EarningsHistoryId", "EF_YearlyFinancialAssessmentId")]
        //[ForeignKey("EF_EarningsHistory")]
        public Nullable<Guid> EF_EarningsHistoryId
        {
            get { return m_EarningsHistoryId; }
            set { m_EarningsHistoryId = value; }
        }

        [NotMapped]
        public YearlyFinancialAssessment EF_EarningsHistory
        {
            get
            {
                if (!m_EarningsHistoryId.HasValue)
                { return null; }
                if (!m_EarningsHistory.Key.Equals(m_EarningsHistoryId.Value))
                { return null; }
                return m_EarningsHistory;
            }
            set
            {
                if (!m_EarningsHistoryId.HasValue)
                { throw new ApplicationException(); }
                if (m_EarningsHistoryId.Value != value.Key)
                { throw new ApplicationException(); }
                m_EarningsHistory = value;
            }
        }

        ICollection<QuerySpecification> IEfAggregate<Company>.OverriddenQueries
        {
            get { return new List<QuerySpecification>(); }
        }

        bool IEfAggregate<Company>.SearchNestedAggregateValues(DbContext context, IParameterizedQuery<Company> query)
        {
            throw new NotImplementedException();
        }

        protected static readonly string YearlyFinancialAssessmentName = (typeof(YearlyFinancialAssessment)).Name;
        void IEfAggregate<Company>.ReadNestedAggregateValues(DbContext context, object rootQueryPredicate, Dictionary<string, object> batchReadState)
        {
            LoadNestedBatchReadState(context, rootQueryPredicate, batchReadState);

            IQueryable<YearlyFinancialAssessment> yearlyFinancialAssessmentResults = (batchReadState[YearlyFinancialAssessmentName] as IQueryable<YearlyFinancialAssessment>).Where<YearlyFinancialAssessment>((YearlyFinancialAssessment yfa) => (yfa.EF_Key == this.m_EarningsHistoryId));
            List<YearlyFinancialAssessment> yearlyFinancialAssessmentList = new List<YearlyFinancialAssessment>(yearlyFinancialAssessmentResults);
            this.EF_EarningsHistory = yearlyFinancialAssessmentList.FirstOrDefault();
            if (this.EF_EarningsHistory != null)
            { this.EF_EarningsHistory.ReadNestedAggregateValues(context, rootQueryPredicate, batchReadState); }
        }

        private void LoadNestedBatchReadState(DbContext context, object rootQueryPredicate, Dictionary<string, object> batchReadState)
        {
            if (batchReadState.ContainsKey(YearlyFinancialAssessmentName))
            { return; }

            string companyName = (typeof(Company)).Name;
            IEnumerable<Company> companies = batchReadState[companyName] as IEnumerable<Company>;
            IEnumerable<Guid> companyGuids = companies.Select((Company com) => com.EF_Key);

            List<Guid> yearlyFinancialAssessmentGuids = new List<Guid>();
            List<Company> companyList = new List<Company>(companies);
            foreach (Company company in companyList)
            {
                if (!company.EF_EarningsHistoryId.HasValue)
                { continue; }

                yearlyFinancialAssessmentGuids.Add(company.EF_EarningsHistoryId.Value);
            }

            Expression<Func<YearlyFinancialAssessment, bool>> yearlyFinancialAssessmentPredicate = null;
            yearlyFinancialAssessmentPredicate = ((YearlyFinancialAssessment yfa) => yearlyFinancialAssessmentGuids.Contains(yfa.EF_Key));
            IQueryable<YearlyFinancialAssessment> yearlyFinancialAssessmentResults = context.Set<YearlyFinancialAssessment>().Where(yearlyFinancialAssessmentPredicate);
            batchReadState.Add(YearlyFinancialAssessmentName, yearlyFinancialAssessmentResults);
        }

        void IEfAggregate<Company>.AddNestedAggregateValues(DbContext context)
        {
            Expression<Func<ICollection<YearlyFinancialAssessment>>> yearlyFinancialAssessmentsGetterExpression = () => (EfAggregateUtilities.ConvertSingleNestedObjectToList(this.EF_EarningsHistory));
            EfAggregateUtilities.AddNestedValues<YearlyFinancialAssessment>(context, yearlyFinancialAssessmentsGetterExpression);
        }

        void IEfAggregate<Company>.UpdateNestedAggregateValues(DbContext context, Company originalAggregateRoot)
        {
            Expression<Func<YearlyFinancialAssessment, Guid>> yearlyFinancialAssessmentKeyGetterExpression = ((YearlyFinancialAssessment nestedObj) => (nestedObj.EF_Key));
            Expression<Func<YearlyFinancialAssessment, YearlyFinancialAssessment, bool>> yearlyFinancialAssessmentMatchExpression = ((YearlyFinancialAssessment obj1, YearlyFinancialAssessment obj2) => ((obj1.EF_Key == obj2.EF_Key)));
            Expression<Func<Company, IDictionary<Guid, YearlyFinancialAssessment>>> yearlyFinancialAssessmentDictionaryGetterExpression = ((Company agg) => (EfAggregateUtilities.ConvertSingleNestedObjectToDictionary(agg.EF_EarningsHistory, yearlyFinancialAssessmentKeyGetterExpression)));
            EfAggregateUtilities.UpdateNestedValues<Company, Guid, YearlyFinancialAssessment>(context, this, originalAggregateRoot, yearlyFinancialAssessmentKeyGetterExpression, yearlyFinancialAssessmentMatchExpression, yearlyFinancialAssessmentDictionaryGetterExpression);
        }

        void IEfAggregate<Company>.DeleteNestedAggregateValues(DbContext context)
        {
            Expression<Func<YearlyFinancialAssessment, bool>> yearlyFinancialAssessmentDeleteQueryExpression = ((YearlyFinancialAssessment yfa) => (yfa.EF_Key == this.EF_EarningsHistoryId));
            EfAggregateUtilities.DeleteNestedValues(context, yearlyFinancialAssessmentDeleteQueryExpression);
        }

        #endregion

        #region AutomotivePricingDataSet Serialization

        string IDataSetSerializable<AutomotivePricingDataSet, DataRow>.ObjectTableName
        {
            get { return AutomotivePricingDataSet.SchemaHelper.Companies.TableName; }
        }

        string IDataSetSerializable<AutomotivePricingDataSet, DataRow>.KeyColumnName
        {
            get { return AutomotivePricingDataSet.SchemaHelper.Companies.EF_CompanyIdColumn.ColumnName; }
        }

        void IDataSetSerializable<AutomotivePricingDataSet, DataRow>.Serialize(AutomotivePricingDataSet datasource, DataRow dataRow)
        {
            ObjectRow objectRecord = dataRow as ObjectRow;

            objectRecord.EF_CompanyId = m_Key;
            objectRecord.SerializeString(m_CeoName, datasource.Companies.CeoNameColumn);
            objectRecord.SerializeString(m_CompanyName, datasource.Companies.CompanyNameColumn);
            objectRecord.SerializeString(m_FounderName, datasource.Companies.FounderNameColumn);
            objectRecord.SerializeNullableField<DateTime>(m_CompanyFoundingDate, datasource.Companies.CompanyFoundingDateColumn);
            objectRecord.SerializeNullableField<Guid>(m_CountryOfHeadquarters, datasource.Companies.CountryOfHeadquartersColumn);
            // LATER: Try to make m_EarningsHistoryId non-nullable since vale generated from DB was non-null
            objectRecord.SerializeNullableField<Guid>(m_EarningsHistoryId, datasource.Companies.EF_EarningsHistoryIdColumn);

            NestedObjectRow nestedObjectRow = datasource.YearlyFinancialAssessments.NewYearlyFinancialAssessmentsRow();
            m_EarningsHistory.Serialize(datasource, nestedObjectRow);
        }

        void IDataSetSerializable<AutomotivePricingDataSet, DataRow>.Deserialize(AutomotivePricingDataSet datasource, DataRow dataRow)
        {
            ObjectRow objectRecord = dataRow as ObjectRow;

            m_Key = objectRecord.EF_CompanyId;
            objectRecord.DeserializeString(out m_CeoName, datasource.Companies.CeoNameColumn);
            objectRecord.DeserializeString(out m_CompanyName, datasource.Companies.CompanyNameColumn);
            objectRecord.DeserializeString(out m_FounderName, datasource.Companies.FounderNameColumn);
            objectRecord.DeserializeNullableField<DateTime>(out m_CompanyFoundingDate, datasource.Companies.CompanyFoundingDateColumn);
            objectRecord.DeserializeNullableField<Guid>(out m_CountryOfHeadquarters, datasource.Companies.CountryOfHeadquartersColumn);
            objectRecord.DeserializeNullableField<Guid>(out m_EarningsHistoryId, datasource.Companies.EF_EarningsHistoryIdColumn);

            m_EarningsHistory.Deserialize(datasource, objectRecord.YearlyFinancialAssessmentsRow);
        }

        #endregion
    }
}